package net.xdclass.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alipay.api.domain.PageInfo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.component.PayFactory;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.constant.CacheKey;
import net.xdclass.constant.TimeConstant;
import net.xdclass.enums.*;
import net.xdclass.exception.BizException;
import net.xdclass.feign.CouponFeignService;
import net.xdclass.feign.ProductFeignService;
import net.xdclass.feign.UserFeignService;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.mapper.ProductOrderItemMapper;
import net.xdclass.mapper.ProductOrderMapper;
import net.xdclass.model.LoginUser;
import net.xdclass.model.OrderMessage;
import net.xdclass.model.ProductOrderDO;
import net.xdclass.model.ProductOrderItemDO;
import net.xdclass.request.*;
import net.xdclass.service.ProductOrderService;
import net.xdclass.util.CommonUtil;
import net.xdclass.util.JsonData;
import net.xdclass.vo.*;
import org.apache.commons.lang.StringUtils;
import org.aspectj.weaver.ast.Var;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : zhen.xue
 * @date : 2021-06-01 15:12
 */
@Slf4j
@Service
public class ProductOrderServiceImpl implements ProductOrderService {


    @Autowired
    private UserFeignService userFeignService;

    @Autowired
    private ProductOrderMapper productOrderMapper;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private ProductOrderItemMapper productOrderItemMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private PayFactory payFactory;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 防重提交
     * 用户微服务-确认收货地址(查询)
     * 商品微服务-获取最新购物项和价格（查询） --清空购物车数据修改，设计到分布式事物可以采用mq最终一致性，或者seata方式进行控制
     * 订单验价
     *  优惠券微服务-获取优惠券
     *  验证价格
     * 锁定优惠券
     * 锁定商品库存
     * 创建订单对象
     * 创建子订单对象
     * 发送延迟消息-用于自动关单
     * 创建支付信息-对接三方支付
     * @param orderRequest
     * @return
     *
     */
    @Transactional
    @Override
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        String orderToken = orderRequest.getToken();
        if(StringUtils.isBlank(orderToken)){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_NOT_EXISTS);
        }
        // 令牌不为空原子操作校验令牌删除令牌
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY, loginUser.getId())), orderToken);
        if(result != null && 0L == result){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL);
        }
        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);
        // 获取收货地址详情
        ProductOrderAddressVO addressVO = this.getUserAddress(orderRequest.getAddressId());
        log.info("收货地址信息:{}",addressVO);
        // 获取加入购物车的商品
        List<Long> productIdList = orderRequest.getProductIdList();
        JsonData cartItemData = productFeignService.confirmOrderCartItems(productIdList);
        List<OrderItemVO> orderItemVoList= cartItemData.getDate(new TypeReference<List<OrderItemVO>>(){});
        log.info("获取的商品：{}",orderItemVoList);
        if(orderItemVoList == null){
            // 购物车商品不存在
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_CART_ITEM_NOT_EXISTS);
        }
        // 验证价格减去商品优惠券
        this.checkPrice(orderItemVoList,orderRequest);
        // 锁定优惠券
        this.lockCouponRecord(orderRequest,orderOutTradeNo);
        // 锁定商品库存
        this.lockProductStock(orderItemVoList,orderOutTradeNo);
        // 创建订单
        ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest, loginUser, orderOutTradeNo, addressVO);
        // 创建订单项
        this.saveProductOrderItems(orderOutTradeNo,productOrderDO.getId(),orderItemVoList);
        // 发送延迟消息，自动关单
        OrderMessage orderMessage = new OrderMessage();
        orderMessage.setOutTradeNo(orderOutTradeNo);
        rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(),rabbitMQConfig.getOrderCloseDelayRoutingKey(),orderMessage);

        // 创建支付
        PayInfoVO payInfoVO = new PayInfoVO(orderOutTradeNo,productOrderDO.getPayAmount(),productOrderDO.getPayType()
                ,orderRequest.getClientType(),orderOutTradeNo,"", TimeConstant.ORDER_PAY_TIMEOUT_MILLIS);

        String payResult = payFactory.pay(payInfoVO);
        if(StringUtils.isNotBlank(payResult)){
            log.info("创建支付订单失败,apyInfoVO = {},payResult = {}",payInfoVO,payResult);
            return JsonData.buildSuccess(payResult);
        }
        log.error("创建支付订单失败,apyInfoVO = {},payResult = {}",payInfoVO,payResult);
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
    }


    /**
     * 新增订单项
     * @param orderOutTradeNo
     * @param orderId
     * @param orderItemVoList
     */
    private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemVoList) {
        List<ProductOrderItemDO> productOrderItemDOList = orderItemVoList.stream().map(obj -> {
            ProductOrderItemDO productOrderItemDO = new ProductOrderItemDO();
            productOrderItemDO.setProductOrderId(orderId);
            productOrderItemDO.setOutTradeNo(orderOutTradeNo);
            productOrderItemDO.setProductId(obj.getProductId());
            productOrderItemDO.setProductName(obj.getProductTitle());
            productOrderItemDO.setProductImg(obj.getProductImg());
            productOrderItemDO.setBuyNum(obj.getBuyNum());
            productOrderItemDO.setCreateTime(new Date());
            productOrderItemDO.setTotalAmount(obj.getTotalAmount());
            productOrderItemDO.setAmount(obj.getAmount());
            return productOrderItemDO;
        }).collect(Collectors.toList());
        productOrderItemMapper.insertBatch(productOrderItemDOList);

    }

    /**
     * 创建订单
     * @param orderRequest
     * @param loginUser
     * @param orderOutTradeNo
     * @param addressVO
     */
    private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductOrderAddressVO addressVO) {
        ProductOrderDO productOrderDO = new ProductOrderDO();
        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        productOrderDO.setCreateTime(new Date());
        // 总价未使用优惠券支付的价格
        productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
        // 实际支付的价格
        productOrderDO.setPayAmount(orderRequest.getRealPayAmount());
        // 需要进行状态检查
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());
        productOrderDO.setNickname(loginUser.getName());
        productOrderDO.setHeadImg(loginUser.getHeadImg());
        productOrderDO.setUserId(loginUser.getId());
        productOrderDO.setDel(0);
        productOrderDO.setOrderType(ProductOrderTypeEnum.DAILY.name());
        productOrderDO.setReceiverAddress(JSON.toJSONString(addressVO));
        productOrderMapper.insert(productOrderDO);
        return productOrderDO;
    }

    /**
     * 锁定商品库存
     * @param orderItemVoList
     * @param orderOutTradeNo
     */
    private void lockProductStock(List<OrderItemVO> orderItemVoList, String orderOutTradeNo) {
        List<OrderItemRequest> orderItemRequestList = orderItemVoList.stream().map(obj -> {
            OrderItemRequest request = new OrderItemRequest();
            request.setBuyNum(obj.getBuyNum());
            request.setProductId(obj.getProductId());
            return request;
        }).collect(Collectors.toList());
        LockProductRequest lockProductRequest = new LockProductRequest();
        lockProductRequest.setOrderOutTradeNo(orderOutTradeNo);
        lockProductRequest.setOrderItemList(orderItemRequestList);
        JsonData jsonData = productFeignService.lockProductStock(lockProductRequest);
        if(jsonData.getCode() != 0){
            log.error("锁定商品库存失败:{}",lockProductRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_LOCK_PRODUCT_FAIL);
        }
    }

    /**
     * 锁定优惠券
     * @param orderRequest
     * @param orderOutTradeNo
     */
    private void lockCouponRecord(ConfirmOrderRequest orderRequest, String orderOutTradeNo) {
        List<Long> lockCouponRecordIds = new ArrayList<>();
        if(orderRequest.getCouponRecordId()>0){
            lockCouponRecordIds.add(orderRequest.getCouponRecordId());;
            LockCouponRecordRequest lockCouponRecordRequest = new LockCouponRecordRequest();
            lockCouponRecordRequest.setLockCouponRecordIds(lockCouponRecordIds);
            lockCouponRecordRequest.setOrderOutTradeNo(orderOutTradeNo);
            // 发起锁定优惠券请求
            JsonData jsonData = couponFeignService.lockCouponRecords(lockCouponRecordRequest);
            if(jsonData.getCode() == null){
                throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
            }
        }
    }


    /**
     * 验证价格
     * 统计全部商品的价格
     * 获取优惠券(判断是否满足优惠券的条件)，总价减去优惠券的价格就是最终的价格
     * @param orderItemVoList
     * @param orderRequest
     */
    private void checkPrice(List<OrderItemVO> orderItemVoList, ConfirmOrderRequest orderRequest) {
        // 统计商品总价
        BigDecimal realPayAmount = new BigDecimal(0);
        for (OrderItemVO orderItemVO : orderItemVoList) {
            BigDecimal totalAmount = orderItemVO.getTotalAmount();
            realPayAmount = realPayAmount.add(totalAmount);
        }
        // 获取优惠券判断是否可以使用
        CouponRecordVO couponRecordVO = getCartCouponRecord(orderRequest);
        // 计算购物车是否满足满减条件
        if(couponRecordVO != null){
            if(realPayAmount.compareTo(couponRecordVO.getConditionPrice()) < 0){
                // 不满足条件
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
            }
            if(couponRecordVO.getPrice().compareTo(realPayAmount) > 0){
                realPayAmount = BigDecimal.ZERO;
            }else{
                realPayAmount = realPayAmount.subtract(couponRecordVO.getPrice());
            }
        }
        // 验价
        if(realPayAmount.compareTo(orderRequest.getRealPayAmount()) != 0){
            log.error("订单验价失败:{}",orderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }

    /**
     * 获取优惠券
     * @param confirmOrderRequest
     * @return
     */
    private CouponRecordVO getCartCouponRecord(ConfirmOrderRequest confirmOrderRequest) {
        Long couponRecordId = confirmOrderRequest.getCouponRecordId();
        if (couponRecordId == null || couponRecordId <= 0) {
            return null;
        }
        JsonData couponRecordData = couponFeignService.findUserCouponRecordById(couponRecordId);
        if(couponRecordData.getCode() != 0){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
        }
        if( couponRecordData.getCode() == 0 ){
            CouponRecordVO couponRecordVO = couponRecordData.getDate(new TypeReference<CouponRecordVO>() {});
            // 判断优惠券是否可用
            if(!couponAvailable(couponRecordVO)){
                log.error("优惠券使用失败，优惠券：{},订单信息:{}",couponRecordVO,confirmOrderRequest);
                throw new BizException(BizCodeEnum.COUPON_UNAVAILABLE);
            }
            return couponRecordVO;
        }
        return null;
    }

    /**
     * 判断优惠券是否可以使用
     * @param couponRecordVO
     * @return
     */
    private boolean couponAvailable(CouponRecordVO couponRecordVO) {
        if(couponRecordVO.getUseState().equalsIgnoreCase(CouponStateEnum.NEW.name())){
            long currentTimeMillis = CommonUtil.getCurrentTimeMillis();
            long endTime = couponRecordVO.getEndTime().getTime();
            long startTime = couponRecordVO.getStartTime().getTime();
            if(currentTimeMillis >= startTime && currentTimeMillis <= endTime){
                return true;
            }
        }
        return false;
    }


    /**
     * 获取收货地址详情
     * @param addressId
     * @return
     */
    private ProductOrderAddressVO getUserAddress(long addressId) {
        JsonData addressData = userFeignService.detail(addressId);
        if(addressData.getCode() != 0){
            log.error("获取收货地址失败,message {}",addressData);
            throw new BizException(BizCodeEnum.ADDRESS_NO_EXIT);
        }
        ProductOrderAddressVO  productOrderAddressVO= addressData.getDate(new TypeReference<ProductOrderAddressVO>(){});
        return productOrderAddressVO;
    }


    @Override
    public String queryProductOrderState(String outTradeNo) {
        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", outTradeNo));
        return productOrderDO == null ? "" :productOrderDO.getState();
    }

    @Override
    public boolean closeProductOrder(OrderMessage orderMessage) {
        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", orderMessage.getOutTradeNo()));
        if(productOrderDO == null){
            // 订单不存在
            log.warn("直接确认消息，订单不存在:{}",orderMessage);
            return true;
        }
        if(productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())){
            log.info("直接确认消息，订单已经支付:{}",orderMessage);
        }
        // 向第三方查询订单是否真的未支付
        PayInfoVO payInfoVO = new PayInfoVO();
        payInfoVO.setPayType(productOrderDO.getPayType());
        payInfoVO.setOutTradeNo(orderMessage.getOutTradeNo());
        String payResult = payFactory.queryPaySuccess(payInfoVO);
        ProductOrderDO orderDO = new ProductOrderDO();
        // 结果为空则未支付，本地取消订单
        if(StringUtils.isBlank(payResult)){
            orderDO.setState(ProductOrderStateEnum.CANCEL.name());
            productOrderMapper.update(orderDO, new QueryWrapper<ProductOrderDO>()
                    .eq("out_trade_no",orderMessage.getOutTradeNo()).eq("state",ProductOrderStateEnum.NEW.name()));
            log.info(" 结果为空则未支付，本地取消订单:{}",orderMessage);
            return true;
        }else{
            // 支付成功，主动把订单状态更改为已经支付，造成该原因可能是支付回调有问题
            log.warn("支付成功，主动把订单状态更改为已经支付，造成该原因可能是支付回调有问题:{}",orderMessage);
            orderDO.setState(ProductOrderStateEnum.PAY.name());
            productOrderMapper.update(orderDO, new QueryWrapper<ProductOrderDO>()
                    .eq("out_trade_no",orderMessage.getOutTradeNo()).eq("state",ProductOrderStateEnum.NEW.name()));
            return true;
        }
    }

    @Override
    public JsonData handlerOrderCallbackMsg(String payType, Map<String, String> paramsMap) {

        if(payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())){
            // 获取商户订单号
            String outTradeNo = paramsMap.get("out_trade_no");
            // 交易状态
            String tradeStatus = paramsMap.get("trade_status");
            if("TRADE_SUCCESS".equalsIgnoreCase(tradeStatus) || "TRADE_FINISHED".equalsIgnoreCase(tradeStatus)){
                // 更新订单状态
                ProductOrderDO orderDO = new ProductOrderDO();
                orderDO.setState(ProductOrderStateEnum.PAY.name());
                productOrderMapper.update(orderDO, new QueryWrapper<ProductOrderDO>()
                        .eq("out_trade_no", outTradeNo).eq("state", ProductOrderStateEnum.NEW.name()));
                return JsonData.buildSuccess();
            }
        }else if(payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())){
            // 微信支付 TODO
        }
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }

    @Override
    public Map<String,Object> findOrderList(int page, int size, String state) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        Page<ProductOrderDO> pageInfo = new Page<>(page,size);
        IPage<ProductOrderDO> productOrderDOPage;
        if(StringUtils.isBlank(state)){
            productOrderDOPage = productOrderMapper.selectPage(pageInfo, new QueryWrapper<ProductOrderDO>().eq("user_id",loginUser.getId()));
        }else{
            productOrderDOPage = productOrderMapper.selectPage(pageInfo, new QueryWrapper<ProductOrderDO>()
                    .eq("user_id",loginUser.getId()).eq("state",state));
        }
        // 获取订单列表
        List<ProductOrderDO> productOrderDOList = productOrderDOPage.getRecords();
        List<ProductOrderVO> productOrderVOList = productOrderDOList.stream().map(orderDO -> {
            List<ProductOrderItemDO> productOrderItemDOList = productOrderItemMapper.selectList(new QueryWrapper<ProductOrderItemDO>().eq("product_order_id", orderDO.getId()));
            List<OrderItemVO> orderItemVOList = productOrderItemDOList.stream().map(item -> {
                OrderItemVO orderItemVO = new OrderItemVO();
                BeanUtils.copyProperties(item, orderItemVO);
                return orderItemVO;
            }).collect(Collectors.toList());
            ProductOrderVO productOrderVO = new ProductOrderVO();
            BeanUtils.copyProperties(orderDO, productOrderVO);
            productOrderVO.setOrderItemVOList(orderItemVOList);
            return productOrderVO;
        }).collect(Collectors.toList());
        Map<String,Object> map = new HashMap<>();
        map.put("total_record",productOrderDOPage.getTotal());
        map.put("total_page",productOrderDOPage.getPages());
        map.put("current_data",productOrderVOList);
        return map;
    }

    @Transactional
    @Override
    public JsonData repay(RepayOrderRequest repayOrderRequest) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>()
                .eq("user_id", loginUser.getId())
                .eq("out_trade_no", repayOrderRequest.getOutTradeNo()));
        log.info("订单状态:{}",productOrderDO);
        if(productOrderDO == null){
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_NOT_EXISTS);
        }
        // 订单状态错误
        if(!ProductOrderStateEnum.NEW.name().equalsIgnoreCase(productOrderDO.getState())){
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_STATE_ERROR);
        }else{
            // 获取订单创建到现在的存活时间
            long orderLiveTime = (CommonUtil.getCurrentTimeMillis() - productOrderDO.getCreateTime().getTime());
            // 创建订单是临界点
            orderLiveTime+=70*1000;
            // 大于订单超时时间则失效
            if(orderLiveTime>=TimeConstant.ORDER_PAY_TIMEOUT_MILLIS){
                return JsonData.buildResult(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT);
            }else{
                // 可以更新订单支付参数payType ,还可以增加订单支付信息日志
                // 总时间 - 存活时间 = 剩下的有效时间
                long timeout = TimeConstant.ORDER_PAY_TIMEOUT_MILLIS - orderLiveTime;
                PayInfoVO payInfoVO = new PayInfoVO(productOrderDO.getOutTradeNo(),productOrderDO.getPayAmount(),repayOrderRequest.getPayType(),repayOrderRequest.getClientType(),
                        productOrderDO.getOutTradeNo(),"",timeout);
                log.info("支付订单payInfoVO:{}",payInfoVO);
                String payResult = payFactory.pay(payInfoVO);
                if(StringUtils.isNotBlank(payResult)){
                    log.info("创建支付订单失败,apyInfoVO = {},payResult = {}",payInfoVO,payResult);
                    return JsonData.buildSuccess(payResult);
                }
                log.error("创建支付订单失败,apyInfoVO = {},payResult = {}",payInfoVO,payResult);
                return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
            }
        }
    }
}